package be.sc.classicnetwork.model;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.ManyToMany;
import javax.persistence.Transient;

import org.acegisecurity.GrantedAuthority;
import org.acegisecurity.GrantedAuthorityImpl;
import org.acegisecurity.providers.encoding.Md5PasswordEncoder;
import org.acegisecurity.userdetails.UserDetails;

import be.sc.classicnetwork.util.Constant;

/**
 * User.
 */
@Entity
public class User extends AbstractEntity implements UserDetails {

	public static final User ANONYMOUS_USER = new User("anonymous",
			"ROLE_ANONYMOUS");

	private static final ThreadLocal<User> currentUser = new ThreadLocal<User>();

	private String password;

	private String email;

	private String username;

	@Transient
	/** The Locale of the current User */
	private Locale locale;

	@ManyToMany(cascade = { CascadeType.ALL })
	private List<Role> roles = new ArrayList<Role>();

	/**
	 * @see AbstractEntity#AbstractEntity()
	 */
	public User() {
		super();
	}

	/**
	 * Creates a <code>User</code> with the specified username.
	 * 
	 * @param username
	 */
	public User(String username) {
		this.username = username;
	}

	/**
	 * Constructor.
	 */
	private User(String username, String role) {
		this(username);
		this.getRoles().add(Role.ANONYMOUS);
	}

	/**
	 * setCurrentUser. Used by the request interceptor on every request.
	 */
	public static void setCurrentUser(User user) {
		currentUser.set(user);
	}

	/**
	 * getCurrentUser.
	 */
	public static User getCurrentUser() {
		return currentUser.get();
	}

	/**
	 * Checks this User is at least SUB_ADMIN.
	 */
	public boolean isAdmin() {
		return isInRole(Constant.ROLE_SUB_ADMIN);
	}

	/**
	 * Checks this User has at least on the specified roles.
	 */
	public boolean isInRole(String... roleNames) {
		if (roles != null) {
			for (Role r : roles) {
				for (String roleName : roleNames) {
					if (r.getRolename().equals(roleName)) {
						return true;
					}
				}
			}
		}
		return false;
	}

	/**
	 * encryptPassword.
	 */
	public String encryptPassword() {
		Md5PasswordEncoder passwordEncoder = new Md5PasswordEncoder();
		this.password = passwordEncoder.encodePassword(this.password, "SALT");
		return this.password;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	/**
	 * @see org.acegisecurity.userdetails.UserDetails#getPassword()
	 */
	public String getPassword() {
		return this.password;
	}

	public void setPassword(String passWord) {
		this.password = passWord;
	}

	/**
	 * @see org.acegisecurity.userdetails.UserDetails#getUsername()
	 */
	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public List<Role> getRoles() {
		return roles;
	}

	public void setRoles(List<Role> roles) {
		this.roles = roles;
	}

	/**
	 * @see org.acegisecurity.userdetails.UserDetails#getAuthorities()
	 */
	public GrantedAuthority[] getAuthorities() {
		ArrayList<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
		for (Role role : this.roles) {
			grantedAuthorities
					.add(new GrantedAuthorityImpl(role.getRolename()));
		}
		return grantedAuthorities.toArray(new GrantedAuthority[0]);
	}

	/**
	 * @see org.acegisecurity.userdetails.UserDetails#isAccountNonExpired()
	 */
	public boolean isAccountNonExpired() {
		return true;
	}

	/**
	 * @see org.acegisecurity.userdetails.UserDetails#isAccountNonLocked()
	 */
	public boolean isAccountNonLocked() {
		return true;
	}

	/**
	 * @see org.acegisecurity.userdetails.UserDetails#isCredentialsNonExpired()
	 */
	public boolean isCredentialsNonExpired() {
		return true;
	}

	/**
	 * @see org.acegisecurity.userdetails.UserDetails#isEnabled()
	 */
	public boolean isEnabled() {
		return true;
	}

	public Locale getLocale() {
		return locale;
	}

	public void setLocale(Locale locale) {
		this.locale = locale;
	}

}
